Repository navigation
feat(libkernel): support microsecond timeout in size-0 _umtx_op - #5887
adriadam10 merged 1 commit into
Conversation
CnRJay
left a comment
There was a problem hiding this comment.
The decompilation is useful: the caller stores a 4-byte microsecond count and passes its address with size 0. Choosing the interpretation by whether the 16 bytes happen to form a valid timespec only works when the neighbouring stack bytes are invalid, though, and it fails silently when they aren't.
UmtxDeadline still reads KernelTimespec first: tv_sec is 8 bytes, the 4-byte microsecond count plus the next 4 bytes of the caller's frame, and tv_nsec is the 8 bytes after that. If those neighbours are zero (a common value for padding or a freshly cleared local), the timespec is valid, the new branch never runs, and tv_sec is the microsecond count read as seconds. param_2 * 1000 for a 20 ms wait becomes a 20,000-second wait, roughly 5.5 hours, and the title hangs instead of stopping with a message. Whether it hangs depends on stack contents, so it can differ between runs and compilers. On main it at least throws.
FreeBSD's _umtx_op treats a size argument up to sizeof(struct timespec) as a timespec. So the question is what Sony's kernel does with size 0, or whether zlqfTyrQSPk converts the microseconds before reaching the syscall. Two ways to make it exact:
- If
PS5Util.prx'szlqfTyrQSPkis a wrapper rather than_umtx_opitself, implement or patch the conversion at that boundary, where the 4-byte microsecond argument is part of its own ABI. The kernel path then keeps FreeBSD's rule. - If the console's
_umtx_opitself reads 4 bytes for size 0, that needs evidence (a PS5 probe, or the kernel's decompiledumtx_copyin_timeout). Then the rule is "size 0 means a 32-bit microsecond count" unconditionally, with no validity heuristic.
Either way, the test should include a microsecond value whose neighbouring bytes are zero, which is the case that goes wrong now.
75760cc to
a450219
Compare
|
@CnRJay I disassembled
I have updated the PR following the second approach:
AI Assisted: Yes |
|
Thanks for the disassembly. It shows One thing to check before this goes in. The entry on Also, the test change removes the checks that a size-0 timeout with too many nanoseconds or negative seconds throws. That's expected now. If no other test covers the explicit-size path, the same two |
a450219 to
4b34a6d
Compare
|
@CnRJay I inspected PPSA12544's binaries (
Regarding the tests:
|
|
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
|
Checked on Linux: Ubuntu 26.04 (WSL2), GCC 15.2.0, Release, this head (
Not checked: Windows, macOS, the title. AI-assisted: yes (Claude Code). |
|
Thanks for checking PPSA12544. If its |
|
Checked the build and the libkernel test suite (175 tests green, |
What
When
_umtx_opis called with a size argument of 0 (sizeArgument == nullptr),UmtxDeadlinepreviously read the timeout pointer as a 16-byteKernelTimespecand threwNotImplemented_nid_no_patch("_umtx_op timeout with size argument 0 that is not a valid timespec")whenever the nanoseconds field fell outside[0, 1e9)or seconds were negative (introduced in #1482).Decompilation of the caller in Metal Gear Solid 4 (PPSA18194,
eboot.binat0x1ac2a50invokingPS5Util.prx'szlqfTyrQSPk) reveals that the caller allocates a 4-byte local variable on the stack storing the timeout in microseconds:Because the stack allocation is only 4 bytes, reading 16 bytes treats 12 bytes of adjacent stack frame data as
tv_nsec, causing_umtx_opto reject it as an invalid timespec and throw.This change handles size-0 timeouts whose fields do not form a valid
timespecby reading them as a 4-byteuint32_tmicrosecond value and converting them to seconds and nanoseconds. This resolves the technical debt noted in #1482 and allows titles using Unity'sPS5Util.prxand SCElibc.prxto wait on the intended duration without throwing.Tested
_umtx_op timeout with size argument 0 that is not a valid timespec not implemented. With this change,_umtx_opsuccessfully waits on the timeout and the game proceeds past the sync barrier into the in-engine presentation loop at 60 FPS.guest_umtx_op_testspassed, verifying that a 20ms microsecond timeout withsizeArgument == nullptrcorrectly waits and times out withGUEST_ETIMEDOUT.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)